package modelo.geometria;

/** Modela un Vector en el plano real, con coordenadas cartesianas x e y. */
public class Vector implements Comparable<Vector>, Cloneable {
	private double x;
	private double y;
	
	
	/*-- CONSTRUCTORES --*/
	
	/** Constructor de copia. */
	public Vector(final Vector otroVector) {
		this.x = otroVector.getX();
		this.y = otroVector.getY();
	}
	
	/** Construye el Vector nulo. */
	public Vector() {
		setX(0);
		setY(0);
	}
	
	/** Construye un Vector recibiendo sus coordenadas cartesianas. */
	public Vector(double x, double y) {
		this.x = x;
		this.y = y;
	}
	
	/**
	 * Construye un Vector recibiendo sus coordenadas polares.
	 * El tercer par�metro no se lee, solo est� para indicar que se llama a este constructor y no al de coordenadas cartesianas.
	 */
	public Vector(double rho, double theta, int indicadorCoordenadasPolares) {
		this.x = rho * Math.cos(theta);
		this.y = rho * Math.sin(theta);
	}
	
	/*-- GETTERS Y SETTERS --*/
	
	public void setX(double x) {
		this.x = x;
	}

	public double getX() {
		return x;
	}

	public void setY(double y) {
		this.y = y;
	}

	public double getY() {
		return y;
	}
	
	/** Devuelve en radianes el angulo con respecto al eje x.
	 * El valor retornado est� en el rango [-Pi,Pi].
	 * Implementado seg�n Math.atan2(x,y) de la biblioteca java.lang.Math.
	 */
	public double getTheta() {
		return Math.atan2(getY(),getX());			
	}

	/** Devuelve la distancia al origen de la posicion de este Vector.*/
	public double getRho() {
		return Math.sqrt(getX() * getX() + getY() * getY());
	}
	
	/** Deja este vector hecho una copia del vector dado. */
	public Vector copiar(final Vector otroVector) {
		setX(otroVector.getX());
		setY(otroVector.getY());
		return this;
	}
	
	/*-- OPERACIONES SOBRE ESTE VECTOR --*/
	
	/** Mueve este vector a la posicion dada. */
	public Vector mover(final Vector nuevaPosicion) {
		this.setX(nuevaPosicion.getX());
		this.setY(nuevaPosicion.getY());
		return this;
	}
	
	/** Suma a este Vector otro Vector. Equivale a desplazarlo. */
	public Vector sumar(final Vector otroVector){
    	this.setX(this.getX() + otroVector.getX());
    	this.setY(this.getY() + otroVector.getY());
    	return this;
	}
	
	/** Resta a este Vector otro Vector. */
	public Vector restar(final Vector otroVector){
    	this.setX(this.getX() - otroVector.getX());
    	this.setY(this.getY() - otroVector.getY());
    	return this;
	}
	
	/** Devuelve la distancia euclidea a otro Vector. */
	public double getDistancia(final Vector otroVector) {
		double diferenciaX = this.getX() - otroVector.getX();
		double diferenciaY = this.getY() - otroVector.getY();
		return Math.sqrt(diferenciaX*diferenciaX + diferenciaY*diferenciaY);
	}
	
	/** Devuelve la distancia euclidea entre las coordenadas x de este vector y otro.
	 * Siempre es positiva.
	 * Comparar con getDiferenciaX(Vector).
	 */
	public double getDistanciaX(final Vector otroVector) {
		return Math.abs(this.getX() - otroVector.getX());
	}
	
	/** Devuelve la distancia euclidea entre las coordenadas y de este vector y otro.
	 * Siempre es positiva.
	 * Comparar con getDiferenciaY(Vector).
	 */
	public double getDistanciaY(final Vector otroVector) {
		return Math.abs(this.getY() - otroVector.getY());
	}
	
	/** Devuelve la resta entre las coordenadas x de este vector y otro.
	 * Puede ser negativa.
	 * Comparar con getDiferenciaX(Vector).
	 */
	public double getDiferenciaX(final Vector otroVector) {
		return this.getX() - otroVector.getX();
	}
	
	/** Devuelve la resta entre las coordenadas y de este vector y otro.
	 * Puede ser negativa.
	 * Comparar con getDistanciaY(Vector).
	 */
	public double getDiferenciaY(final Vector otroVector) {
		return this.getY() - otroVector.getY();
	}

	/** Devuelve el angulo entre este Vector y el dado en los parametros. 
	 * El resultado esta en el rango [0,Pi].
	 */
	public double getAngulo(final Vector otroVector) {
		double cosAngulo = Vector.productoEscalar(this, otroVector) / this.getRho() / otroVector.getRho();
		return Math.acos(cosAngulo);
	}	
	
	/** Multiplica este vector por un escalar. */
	public Vector multiplicar(double escalar) {
		setX(getX() * escalar);
		setY(getY() * escalar);
		return this;
	}
	
	/** 
	 * Traslada este Vector con respecto al origen, seg�n los radianes dados.
	 * Siguiendo una orbita circular.
	 */
	public Vector trasladar(double radianes) {
		double rho = getRho();
		double theta = getTheta();
		
		double x = rho * Math.cos(theta + radianes);
		double y = rho * Math.sin(theta + radianes);
		
		setX(x);
		setY(y);
		
		return this;
	}
	
	/** 
	 * Traslada este Vector con respecto a la posicion dada por otro Vector, y seg�n los radianes dados.
	 * Siguiendo una orbita circular.
	 */
	public Vector trasladar(Vector centroTraslacion, double radianes) {

		// desplazo al origen...
		this.restar(centroTraslacion);

		// traslado...
		this.trasladar(radianes);
		
		// restituyo a la posicion original, pero trasladado...
		this.sumar(centroTraslacion);

		return this;
	}
	
	/*-- OPERACIONES ESTATICAS QUE NO MODIFICAN A ESTE VECTOR --*/
	
	/** Retorna un Vector que es la suma entre los dos Vectores dados. */
	public static Vector suma(final Vector vector1, final Vector vector2) {
    	Vector vectorResultado = new Vector (
    			vector1.getX() + vector2.getX(),
    			vector1.getY() + vector2.getY()
    			);
    	return vectorResultado;
	}
	
	/** Retorna un Vector que es la resta entre los dos Vectores dados. */
	public static Vector resta(final Vector vector1, final Vector vector2) {
    	Vector vectorResultado = new Vector (
    			vector1.getX() - vector2.getX(),
    			vector1.getY() - vector2.getY()
    			);
    	return vectorResultado;
	}
	
	/** Retorna un Vector que es el dado multplicado por el escalar dado. */
	public static Vector multiplicacion(final Vector vector, double escalar) {
		Vector vectorResultado = new Vector (
				vector.getX() * escalar,
				vector.getY() * escalar
				);
		return vectorResultado;
	}
	
	/**
	 * Retorna un Vector que es la traslacion del dado respecto al origen, seg�n los radianes dados.
	 * Y siguiendo una orbita circular.
	 */
	public static Vector traslacion(final Vector vector, double radianes) {
		double rho = vector.getRho();
		double theta = vector.getTheta();
		
		Vector vectorResultado = new Vector (
				rho * Math.cos(theta + radianes),
				rho * Math.sin(theta + radianes)
				);
		
		return vectorResultado;
	}
	
	/** Retorna el producto escalar entre dos vectores. */
	public static double productoEscalar(final Vector vector1, final Vector vector2) {
		return vector1.getX() * vector1.getX() + vector1.getY() * vector2.getY();
	}

	/*--- METODOS DE INTERFAZ Comparable<Vector> ---*/
	
	@Override
	public int compareTo(Vector posicion) {
		if (this.x == posicion.getX() && this.y == posicion.getY())
			return 0;
		else if (this.x < posicion.getX() && this.y < posicion.getY())
			return -1;
		else 
			return 1;
	}
	
	/*--- METODOS DE INTERFAZ Cloneable ---*/
	
	public Object clone()
	{
	    Object clone = null;
	    try 
	    {
	        clone = super.clone();
	    } 
	    catch(CloneNotSupportedException e)
	    {
	    	//nunca sucede
	    }
	    return clone;
	}

	@Override
	public String toString() {
		return String.valueOf(getX())+","+String.valueOf(getY());
	}
}
